SCSI Driver
Volume Number: 4
Issue Number: 2
Column Tag: Forth Forum
A SCSI Driver in Forth
By Jörg Langowski, MacTutor Editorial Board, Grenoble, France
“An experimental SCSI driver in Mach2”
This column almost didn’t make it in time. Believe it or not, but while testing the
SCSI driver of this article, my Quantum Q280 gave up. No, read on, it wasn’t the
software. (How could it be MY software, anyway?) The interface board let go, and
there was my most recent version of the driver, on the disk, inaccessible, me dummy
having made no backup for the last 2 days. Left with ‘only’ a 20 MByte Seagate drive, I
was going through a non-trivial exercise on how to debug a SCSI driver on the same
disk which is used for developing it. You cannot imagine the strange things that can
happen when booting from a disk with a partially functioning driver; it sometimes
forces you into doing absolute no-nos such as booting from a floppy and attaching the
SCSI connector afterwards so you can re-install the driver without getting la bombe
immediately. At least, the development cycle is slowed down appreciably, since every
other time you’ll have to boot from a floppy.
Anyway, here it is. The listing contains the Forth source of a SCSI driver which
is more or less a translation of Apple’s assembly source into Mach2. It is quite long,
and I’m not going through all of it in detail, having explained the function of most of the
routines in my last column. One added feature is error retry; if a SCSI error happens,
the offending command is repeated up to ten times.
The driver takes 50% more space than the assembly version and is about half as
fast. So, you may ask, why go through all this trouble? You might have noticed the title
‘An experimental SCSI driver’. The code is meant for you to experiment with, add
features - such as better error recognition and correction, multi- volume support,
anything you may imagine. Or translate part of the routines into C or assembler. I just
felt it was necessary to have a functioning example of a SCSI driver in some
‘higher-level’ language to make changes easier.
From the Forth point of view, I added a number of compiling words and glue
routines that may be helpful for you in developing other things.
You already know the words :XDEF, ;XDEF, and XLEN from the Hypercard article.
For easier programming of drivers and desk accessories, I now added :DA, ;DA, and
DALEN which define the header of a driver, fill in the header after its definition is
complete, and return the total length of the driver.
Since the SCSI driver code is jumped to at the beginning, it looks like other
definition procedures from the outside, and its definition is therefore embedded
between :XDEF and ;XDEF. In its interior, the SCSI driver contains a real DRVR, with is
bracketed between :DA and ;DA. You recognize the glue code for the Open, Close,
Control, Status and Prime routines at the end of the listing; the glue routines have been
simplified by introducing macros for register save/restore and eventual jumps to the
JIODone routine.
The Prime routine
Prime is the only one that has to be explained in more detail here since it has
been modified with respect to the description given in the last issue. I added an error
retry loop, and transfer of the SCSI data in variable size chunks. The code as printed
takes 127 sectors on each read or write, that is, any amount of data larger than that
will be transferred using several calls to the SCSICommon routine. This has been
worked out empirically; larger transfer sizes would not work on the ST225N ( which
would result in files > 64K not being copied on a Finder copy). Your disk drive may
allow larger sizes or need smaller ones; check this out if you like. You might also want
to change the Read/Write Extended to a simple Read/Write command, or use a
multi-transfer SCSI instruction block. The driver is certainly not generic and might
need such adjustments to work on your disk.
SCSI Installation
The installation part has been changed slightly, and you’ll have to replace parts
of the installer code given in the last column. First, I changed the device and partition
maps such that the driver starts at block 4 and the Macintosh volume partition at block
16. This is where the Apple Hard Disk installation program seems to expect it,
therefore you can easily replace this driver with the Apple driver in case of problems,
without haveing to reformat the disk. Furthermore, I added a small routine that will
install the driver code in the system heap and call it so that it gets installed in the unit
table; this is an easy way of testing the driver without writing it to disk. You can even
install the same disk that you are using to write the program, in which case you will
be left with two drive icons on the desktop, referring to the same disk. For testing, this
is OK, only don’t work too much in such an awkward configuration.
I hope this series of two articles has given you some ideas on how to write your
own SCSI driver for your particular device. If someone comes up with a good examples
for controlling a tape streamer - we’re always open for contributions, as you know.
Feedback Dept.
Someone read my complaints about the missing editor in Mach2; a letter with a
disk arrived here recently:
“Dear Mr. Langowski,
I’m afraid I’m not a fan of Forth [Oh JL]. I like the idea of a stack-based
language but there’s an impenetrable jargon barrier Besides, I just don’t like typing
shifted characters, like #, !, % and so on. So I stay home with Pascal and Assembler.
On the other hand, I always at least glance at your column in MacTutor (as well as
every other article that isn’t Mac II- specific). In the October issue, you say, ‘It is a
shame that a powerful development system like Mach2 still lacks a reasonable
integrated editor. At least multiple windows should be possible My standard
development system is Mach2.13 with its editor and Mockwrite, plus Edit under
Switcher if required.’ I’m stunned. The idea of using Mockwrite in a development
system! The Mach2 editor must be really bad [ It’s not bad for what it does, i.e., has no
major bugs; but I admit its features are limited - JL].
I had an editor problem myself, working with TML Pascal. TML won’t really run
under the Switcher; it demands too much memory. So if I make a syntax error, I have
to transfer to the editor, which means quitting the compiler and launching the editor,
fix the error, then quit the editor and re-launch the compiler. Then I can re-compile.
Something of a pain, particularly since error-free code is somewhat rare, at least
when I’m the coder! [This sounds familiar. JL] My solution: Afterthought, a desk
accessory programming editor. Now I fix bugs without leaving the compiler.
My problem is solved, and yours may be as well. I’m sending you a copy of
Afterthought. It will open large files (up to 8 Megabytes), two at a time. It has most of
the features of MDS Edit - the most significant exception is Replace All. And it’s
reasonably fast. At the very least, it is better than Mockwrite!
Sincerely,
Clifford Story, Jimmy Mac Software, P.O.Box 957, Murfreesboro, Tennessee
37133.”
Thank you, Clifford, for developing a product that - I guess - many of our
readers have been waiting for.
The disk contained the Afterthought desk accessory, its manual, some update notes
and examples, a demo version, and a demo version of Idealiner, an idea processor
written by the same author. The prices are, by the way, extremely reasonable; $20
for the editor and $40 for the idea processor. To find out more about these products,
send mail to the above address, or GEnie mail to CLIFF.
The second editor desk accessory product that I find worth mentioning is
JoliWrite, written by Benoît Widemann from Paris, a small (32K limit) text editor
that is extremely useful for working with bulletin board services. Often, when you
prepare a text off line for a BBS message, you wish to be able to enter the text
free-format and then format into lines of so and so many characters, with paragraph
indentation and justification if desired. Also, over here it is often necessary to convert
accentuated characters from Macintosh into ASCII representation and back, and in
general you might wish to be able to remove/add line feeds, clip off numbers from line
starts, etc. JoliWrite does all that, and in addition is one of the few products that
supports Undo on all operations. By the time you read this, an English translation of
JoliWrite (shareware, 120F/US$20) will hopefully be available on the Macintosh
section of GEnie.
Listing 1: An experimental SCSI driver
( © 1987 J. Langowski / MacTutor )
only forth definitions
also mac also assembler
CODE SCALE
MOVE.L (A6)+,D0
BMI.S @1
MOVE.L (A6),D1
ASL.L D0,D1
MOVE.L D1,(A6)
RTS
@1 MOVE.L (A6),D1
NEG.L D0
ASR.L D0,D1
MOVE.L D1,(A6)
RTS
END-CODE
: 4ASCII 0
4 0 DO
8 SCALE 0 WORD 1+ C@ + LOOP
;
( *** compiler support words for external definitions *** )
: :xdef
create -4 allot
$4EFA w, ( JMP )
0 w, ( entry point to be filled later )
0 , ( length of routine to be filled later )
here 6 - 76543
;
: ;xdef { branch marker entry | -- }
marker 76543 <> abort” xdef mismatch”
entry branch - branch w!
here branch - 2+ branch 2+ !
;
: xlen 4 + @ ; ( get length word of external definition )
( *** driver header block *** )
0 CONSTANT drvrFlags
2 CONSTANT drvrdelay
4 CONSTANT drvrEMask
6 CONSTANT drvrMenu
8 CONSTANT drvrOpen
10 CONSTANT drvrPrime
12 CONSTANT drvrCtl
14 CONSTANT drvrStatus
16 CONSTANT drvrClose
18 CONSTANT drvrname
50 CONSTANT DAlength
( *** compiler support words for DA and driver definitions *** )
: :DA
create -4 allot
here 87654 ( start of DA block, and marker )
54 allot ( length of block )
;
: ;DA { DAstart marker Ropen Rprime Rctl Rstatus Rclose
Rflags Rdelay Remask Rmenu Rname | -- }
marker 87654 <> abort” DA definition mismatch”
Ropen DAStart - DAStart drvrOpen + w!
Rprime DAStart - DAStart drvrPrime + w!
Rctl DAStart - DAStart drvrCtl + w!
Rstatus DAStart - DAStart drvrStatus + w!
Rclose DAStart - DAStart drvrClose + w!
Rflags DAStart drvrFlags + w!
Rdelay DAStart drvrDelay + w!
Remask DAStart drvrEmask + w!
RMenu DAStart drvrMenu + w!
Rname count dup DAStart drvrName + c!
DAStart drvrName + 1+ swap
dup 31 > if drop 31 then cmove
here DAstart - DAStart DAlength + !
;
: DAlen DAlength + @ ; ( get length word of external definition )
\ ------------------------------------------------------
\ some macros needed in the driver
\ ------------------------------------------------------